package lambda;

import com.sun.deploy.util.StringUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SteamDemo {
    /**
     * 惰性求值与及早求值
     */
    public static void streamAndList(List<String> names) {
        // 惰性求值方法:
        // 这行代码并未做什么实际性的工作，filter只是描述了Stream，没有产生新的集合
        Stream<String> stream = names.stream().filter(s -> s.contains("hello"));
        // 及早求值方法:
        // collect最终会从Stream产生新值，拥有终止操作。
        List<String> list = stream.collect(Collectors.toList());
        // 理想方式是形成一个惰性求值的链，最后用一个及早求值的操作返回想要的结果，与建造者模式相似。
        list.forEach(System.out::println);
    }

    /**
     * Filter 示例
     */
    public static void filterDemo() {
        List<String> stringData = Arrays.asList("test1", "test2", "test3", "hello1", "hello2", "ILoveJAVA");
        // filter 使用Predicate函数式接口
        Predicate<String> startsWithTest = (n) -> n.startsWith("test");
        System.out.println("筛选出List中test开头的字符串:");
        stringData.stream().filter(startsWithTest).forEach(System.out::println);
        // 可以用and()、or()等逻辑函数来合并Predicate
        Predicate<String> sixLetterLong = (n) -> n.length() == 6;
        System.out.println("筛选出List中test开头或长度为6的字符串:");
        stringData.stream().filter(startsWithTest.or(sixLetterLong)).forEach(System.out::println);
    }

    /**
     * Map 示例
     */
    public static void mapDemo() {
        List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
        // map 使用Function函数式接口
        Function<Integer, Double> function = cost -> cost + 0.12 * cost;
        System.out.println("生成新的Double类型的列表，1.12倍:");
        costBeforeTax.stream().map(function).forEach(System.out::println);
    }

    /**
     * Reduce
     */
    public static void reduceDemo() {
        List<Integer> money = Arrays.asList(100, 200, 300, 400, 500);
        // reduce 使用BinaryOperator函数式接口，BiFunction的一种
        BinaryOperator<Integer> binaryOperator = (sum, money1) -> sum + money1;
        int sum = money.stream().reduce(binaryOperator).get();
        // int 的话也可以直接用方法引用
        sum = money.stream().reduce(Integer::sum).get();
        System.out.println("Total : " + sum);
    }

    /**
     * Collectors
     */
    public static void collectorsDemo() {
        List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
        System.out.println("将字符串换成大写:");
        // 常规的toList, toSet, toMap
        List<String> result = G7.stream().map(String::toUpperCase).collect(Collectors.toList());
        result.forEach(System.out::println);
        System.out.println("将字符串换成大写并用逗号链接起来:");
        String resultText = G7.stream().map(String::toUpperCase).collect(Collectors.joining(", "));
        System.out.println(resultText);
    }

    /**
     * flatMap
     */
    public static void flatMapDemo() {
        List<String> letters = Arrays.asList("Hello","World");
        // flatMap 把数组流中的每一个值，使用所提供的函数执行一遍，得到一个子数组流
        // 最后将所有子数组流整合成一个数组流，完成一对多的转变
        Function<String, Stream<String>> function = s -> {
            String[] array = s.split("");
            return Arrays.stream(array);
        };
        letters.stream().flatMap(function).forEach(System.out::println);
        // 与Map的不同点是，map一对一的，得到元素个数相同的数组流
        // flatMap可以一对多，得到的元素个数大概率会和原数组流的个数不同
        Stream<String> streamAfterFlatMap = letters.stream().flatMap(function);
        Stream<Stream<String>> streamAfterMap = letters.stream().map(function);
    }

    public static void main(String[] args) {
        flatMapDemo();
    }
}
